package study.alex.resumedb.gae;

import com.vaadin.ui.*;
import study.alex.resumedb.gae.model.GoogleDriveResumeBlob;
import study.alex.resumedb.gae.model.Resume;

import java.io.*;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Properties;
import java.util.logging.Logger;

/**
 * Created with IntelliJ IDEA.
 * User: alex
 * Date: 31.01.14
 * Time: 19:23
 * SVN $Id$
 */
public class BlobUploadReceiver extends AbstractComponent implements Upload.Receiver, Upload.FinishedListener, Upload.FailedListener, Upload.SucceededListener, Upload.StartedListener {
    private static final Logger log = Logger.getLogger(BlobUploadReceiver.class.getName());

    private ResumeBlob blob;
    private ResumeEditor parentWindow;
    private final Resume resumeItem;
    private String name;
    private String type;
    private ByteArrayOutputStream buffer;



    @Override
    public void uploadFailed(Upload.FailedEvent event) {
        log.info("Upload failed. Reason: " + event.getReason());
        Notification.show(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("upload.failed"), event.getReason().getMessage(), Notification.Type.WARNING_MESSAGE);
        if (parentWindow != null) parentWindow.saveEnable(true);
    }

    @Override
    public void uploadFinished(Upload.FinishedEvent event) {
        log.info("Upload finished. Event: " + event.toString());
    }

    @Override
    public OutputStream receiveUpload(String filename, String mimeType) {

        log.info("Upload file name: " + filename + " type: " + mimeType);
        name = filename;
        type = mimeType;

        InputStream in = BlobUploadReceiver.class.getResourceAsStream("mime-types.properties");
        if (in != null) {
            log.fine("Load mime types properties");
            Properties mimeTable = new Properties();
            try {
                mimeTable.load(in);
            } catch (IOException e) {
                log.fine("Can't load mime properties\n" + e);
            } finally {
                try {
                    in.close();
                } catch (IOException e) {
                    log.fine("Can't close mime properties input stream\n" + e);
                }
            }

            String extention = filename.substring(filename.lastIndexOf('.'), filename.length()).toLowerCase();

            String newMimeType = mimeTable.getProperty(extention);
            if (newMimeType != null) type = newMimeType;
            log.fine("New mime type: " + type);
        }

        buffer = new ByteArrayOutputStream();

        return buffer;
    }



    @Override
    public void uploadSucceeded(Upload.SucceededEvent event) {
        int sz = buffer.size();

        log.info("Upload successful.  File size is " + sz);
        try {
            if (sz <= blob.getMaxSize()) {
                if (blob.storeBlob(buffer.toByteArray(), type, name)) {
                    log.fine("Blob object is " + blob);
                    log.fine("Blob Id is " + blob.getId());
                    log.fine("Blob type is " + blob.getType());
                    fireEvent(new ReceiverEvent(this, blob));
                    Notification.show(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("upload.successful"), Notification.Type.HUMANIZED_MESSAGE);
                    if (parentWindow != null) parentWindow.saveEnable(true);
                } else {
                    log.fine("Failed store Blob");
                    throw new IOException("Failed store Blob");
                }
            } else {
                log.info("Max blob size exceeded.  Max size is " + blob.getMaxSize());
            }
        } catch (Exception e) {
            Notification.show(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("upload.error"), e.getMessage(), Notification.Type.ERROR_MESSAGE);
        }

    }

    public BlobUploadReceiver(Resume item, ResumeEditor parent) {
        this.resumeItem = item;
        this.parentWindow = parent;
        log.fine("New receiver.  Resume item is " + resumeItem);
    }

    @Override
    public void uploadStarted(Upload.StartedEvent event) {

        try {
            String user = resumeItem.getUserId();
            blob = resumeItem.getBody();
            if (blob == null) blob = new GoogleDriveResumeBlob(user, resumeItem);
        } catch (GoogleDriveResumeBlob.GoogleDriveNotAvailableException e) {
            log.warning(e.getCause().toString());
            Notification.show(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("create.new.blob.error"), e.getMessage(), Notification.Type.ERROR_MESSAGE);
            event.getUpload().interruptUpload();
            return;
        } catch (GoogleDriveResumeBlob.GoogleDriveResumeBlobException e) {
            log.warning(e.getCause().toString());
            Notification.show(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("create.new.blob.error"), e.getMessage(), Notification.Type.ERROR_MESSAGE);
            event.getUpload().interruptUpload();
            return;
        } catch (IOException e) {
            log.warning(e.getCause().toString());
            Notification.show(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("create.new.blob.error"), e.getMessage(), Notification.Type.ERROR_MESSAGE);
            event.getUpload().interruptUpload();
            return;
        }

        long max = 0L;
        try {
            max = blob.getMaxSize();
        } catch (Exception e) {
            event.getUpload().interruptUpload();
            Notification.show(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("upload.error"), e.getMessage(), Notification.Type.ERROR_MESSAGE);
            return;
        }
        Long size = event.getContentLength();

        if (size > max) {
            log.info("File too long.  Max size is " + max);
            event.getUpload().interruptUpload();
            Notification.show(MessageFormat.format(((ResumeDBMaintUI) UI.getCurrent()).getBundle().getString("file.too.long.max.size.is.0"), max), Notification.Type.ERROR_MESSAGE);
            return;
        }
        if (parentWindow != null) parentWindow.saveEnable(false);
    }

    public void addListener(ReceivedListener listener) {
        try {
            Method method = ReceivedListener.class.getDeclaredMethod(
                    "blobSucceeded", new Class[] { ReceiverEvent.class });
            addListener(ReceiverEvent.class, listener, method);
        } catch (final java.lang.NoSuchMethodException e) {
            // This should never happen
            throw new java.lang.RuntimeException(
                    "Internal error, editor saved method not found");
        }
    }

    public void removeListener(ReceivedListener listener) {
        removeListener(ReceiverEvent.class, listener);
    }

    public static class ReceiverEvent extends Component.Event {

        private ResumeBlob receivedItem;

        public ReceiverEvent(Component source, ResumeBlob savedItem) {
            super(source);
            this.receivedItem = savedItem;
        }

        public ResumeBlob getReceivedBlob() {
            return receivedItem;
        }
    }

    public interface ReceivedListener extends Serializable {
        public void blobSucceeded(ReceiverEvent event);
    }
}
